package com.codemes.happylist.modules.pdf;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.codemes.happylist.CodemesToolMain;
import com.codemes.happylist.config.ThreadPoolConfig;
import com.codemes.happylist.modules.pdf.service.HtmlToPdfUtil;
import com.codemes.happylist.modules.pdf.view.DomTreeItem;
import com.codemes.happylist.utils.DocumentUtil;
import com.codemes.happylist.views.loading.CodemesLoading;
import de.felixroske.jfxsupport.FXMLController;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebView;
import javafx.stage.FileChooser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.StringUtils;
import org.w3c.dom.*;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

/**
 * @author nscoffee
 */
@FXMLController
@Slf4j
public class HtmlToPdfController implements Initializable {
    @FXML
    public TabPane tabPanePdfTool;
    @FXML
    public Tab tabHtmlToPdf;
    @FXML
    public TextField tfHtmlLink;
    @FXML
    public Button btnGoToLink;
    @FXML
    public WebView webViewHtml;
    @FXML
    public TreeView<Node> documentTreeView;
    @FXML
    public AnchorPane pdfContainer;
    @FXML
    public AnchorPane spTopPane;
    @FXML
    public AnchorPane spBottomPane;
    @FXML
    public SplitPane spContainer;
    @FXML
    public HBox hbAddressBar;
    @FXML
    public Button btnGetDocWidth;

    @Resource(name = ThreadPoolConfig.POOL_BEAN_NAME)
    ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private static final String[] ignoreClickTags = {
            "script","style","head","meta","link","title","noscript"
    };
    private static final  String maskDivId = "codemes_mask_div";
    private static final  String targetAttrName = "id";

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        tabPanePdfTool.prefWidthProperty().bind(pdfContainer.widthProperty());
        tabPanePdfTool.prefHeightProperty().bind(pdfContainer.heightProperty());

        spContainer.prefHeightProperty().bind(tabPanePdfTool.heightProperty().subtract(hbAddressBar.getPrefHeight()));
        spContainer.setLayoutY(hbAddressBar.getPrefHeight());

        webViewHtml.prefWidthProperty().bind(spTopPane.widthProperty());
        webViewHtml.prefHeightProperty().bind(spTopPane.heightProperty());
        webViewHtml.getEngine().getLoadWorker().stateProperty().addListener((observableValue, state, t1) -> {
            log.info("load state -> {} {} {}",observableValue,state,t1);
            if(!t1.equals(Worker.State.SUCCEEDED)){
                return;
            }
            Document document = webViewHtml.getEngine().getDocument();
            Element element = document.getDocumentElement();
            DomTreeItem<Node> rootItem = new DomTreeItem<>(element);
            documentTreeView.setRoot(rootItem);
            initDocumentTree(document,rootItem,element.getChildNodes());
        });

        documentTreeView.prefWidthProperty().bind(spBottomPane.widthProperty());
        documentTreeView.prefHeightProperty().bind(spBottomPane.heightProperty());
        documentTreeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<Node>>() {
            @Override
            public void changed(ObservableValue<? extends TreeItem<Node>> observableValue, TreeItem<Node> nodeTreeItem, TreeItem<Node> t1) {
                changeMaskPosition((DomTreeItem<Node>) t1);
            }
        });
        documentTreeView.setCellFactory(nodeTreeView -> {
            nodeTreeView.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, e -> {
                if (e.getTarget() instanceof TextFieldTreeCell) {
                    TextFieldTreeCell<Node> textFieldTreeCell = (TextFieldTreeCell<Node>) e.getTarget();
                    DomTreeItem<Node> hoverNode = (DomTreeItem<Node>) textFieldTreeCell.getTreeItem();
                    changeMaskPosition(hoverNode);
                }
            });
            nodeTreeView.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, e -> {
                if (e.getTarget() instanceof TextFieldTreeCell) {
                    resetMaskPosition();
                }
            });
            return new TextFieldTreeCell<>();
        });


        ContextMenu treeCtxMenu = initDomTreeContextMenu();

        documentTreeView.setContextMenu(treeCtxMenu);
        btnGoToLink.setOnAction(event -> {
            String link = tfHtmlLink.getText();
            if(StringUtils.isEmpty(link)){
                link = "http://www.baidu.com";
            }
            log.info("load url -> {}",link);
            webViewHtml.getEngine().load(link);
        });

        btnGetDocWidth.setOnAction(e -> {
            Object width = webViewHtml.getEngine().executeScript("document.body.offsetWidth");
            log.info("document width -> {}",width);
        });
    }

    /**
     * 为domTree设置右键菜单
     * @return
     */
    private ContextMenu initDomTreeContextMenu() {
        String treeCtxMenuClass="tree-ctx-menu";
        ContextMenu treeCtxMenu = new ContextMenu();
        MenuItem convertToPdf = new MenuItem("转换为PDF");
        convertToPdf.getStyleClass().add(treeCtxMenuClass);
        treeCtxMenu.getItems().add(convertToPdf);
        convertToPdf.setOnAction(e -> {
            DomTreeItem<Node> selectTreeItem = (DomTreeItem<Node>) documentTreeView.getSelectionModel().getSelectedItem();
            Node selectedNode = selectTreeItem.getNode();
            Node idAttr = selectedNode.getAttributes().getNamedItem(targetAttrName);
            if(null==idAttr){
                idAttr = createAttributte(targetAttrName);
                selectedNode.getAttributes().setNamedItem(idAttr);
            }
            String idValue = idAttr.getNodeValue();
            //选择保存文件，
            FileChooser fileChooser = new FileChooser();
            File destFile = fileChooser.showSaveDialog(CodemesToolMain.mainStage);
            if(null==destFile){
                return;
            }
            // 计算页面宽度
            Object widthStr = webViewHtml.getEngine().executeScript("document.getElementById('"+idValue+"').offsetWidth");
            Integer width = null;
            if(null!=widthStr&& NumberUtil.isNumber(widthStr.toString())){
                width = Integer.valueOf(widthStr.toString());
            }

            Integer finalWidth = width;
            CodemesLoading codemesLoading = CodemesLoading.of(CodemesToolMain.mainStage,
                    "转换中", 30, new Task<Object>() {

                        @Override
                        protected Object call() throws Exception {
                            String location = webViewHtml.getEngine().getLocation();
                            try {
                                HtmlToPdfUtil.htmlToPdf(location,selectedNode.getOwnerDocument(),idValue,destFile, finalWidth);
                                Platform.runLater(() -> {
                                    Alert alert = new Alert(Alert.AlertType.INFORMATION,"转换完成>"+destFile.getAbsolutePath());
                                    alert.show();
                                });

                            } catch (IOException | InterruptedException ex) {
                                log.error("convert html to pdf error -> {}",e);
                                Thread.currentThread().interrupt();
                                Platform.runLater(() -> {
                                    Alert alert = new Alert(Alert.AlertType.ERROR,"转换失败>"+ex.getMessage());
                                    alert.show();
                                });
                            }
                            return null;
                        }
                    });
            codemesLoading.show();
        });

        MenuItem copyItem = new MenuItem("复制outerHTML");
        copyItem.getStyleClass().add(treeCtxMenuClass);
        treeCtxMenu.getItems().add(copyItem);
        copyItem.setOnAction(e -> {

            //TODO 实现对剪切板的访问
            Alert alert = new Alert(Alert.AlertType.INFORMATION,"点击复制按钮->"+e.getSource());
            alert.show();
        });
        return treeCtxMenu;
    }

    /**
     * 重置蒙层的位置
     */
    private void resetMaskPosition() {
        String script =  "var mask = document.getElementById(\"" + maskDivId + "\");\n" +
//                        .append("mask.innerHTML=target.offsetTop+' offsetLeft='+target.offsetLeft+' offsetHeight='+target.offsetHeight+' offsetWidth='+target.offsetWidth+' clientHeight='+target.clientHeight+' clientWidth='+target.clientWidth+' clientTop='+target.clientTop+' clientLeft='+target.clientLeft;\n")
                "if(mask){\n" +
                "mask.style.left='0px';\n" +
                "mask.style.top='0px';\n" +
                "mask.style.height='0px';\n" +
                "mask.style.width='0px';\n"+
                "}";
        webViewHtml.getEngine().executeScript(script);
    }

    /**
     * 根据鼠标所在的元素，变更蒙层的位置
     * @param onItem
     */
    private void changeMaskPosition(DomTreeItem<Node> onItem) {
        if(null==onItem){
            return;
        }
        // 新选中元素设置样式
        Node selectedNode = onItem.getNode();
        String tag = selectedNode.getNodeName();
        if(ArrayUtil.contains(ignoreClickTags,tag.toLowerCase())){
            return;
        }
        NamedNodeMap nodeAttributes = selectedNode.getAttributes();
        if(null==nodeAttributes){
            return;
        }
        if(tag.equalsIgnoreCase("input")){
            Node inputTypeAttr = nodeAttributes.getNamedItem("type");
            if(inputTypeAttr.getNodeValue().equalsIgnoreCase("hidden")){
                log.info("input hidden 类型元素不进行处理->>{}",onItem.toHtml());
                return;
            }
        }

        Node idAttrNode = nodeAttributes.getNamedItem(targetAttrName);
        if (idAttrNode==null){
            idAttrNode = createAttributte(targetAttrName);
            selectedNode.getAttributes().setNamedItem(idAttrNode);
        }
        String idValue = idAttrNode.getNodeValue();

        String script = "var target = document.getElementById(\"" + idValue + "\");\n" +
                "var mask = document.getElementById(\"" + maskDivId + "\");\n" +
//                        .append("mask.innerHTML=target.offsetTop+' offsetLeft='+target.offsetLeft+' offsetHeight='+target.offsetHeight+' offsetWidth='+target.offsetWidth+' clientHeight='+target.clientHeight+' clientWidth='+target.clientWidth+' clientTop='+target.clientTop+' clientLeft='+target.clientLeft;\n")
                "if(mask){\n" +
                "mask.style.left=target.offsetLeft+'px';\n" +
                "mask.style.top=target.offsetTop+'px';\n" +
                "mask.style.height=target.offsetHeight+'px';\n" +
                "mask.style.width=target.offsetWidth+'px';\n"+
                "}";
        webViewHtml.getEngine().executeScript(script);
    }

    private Node createAttributte(String attrName) {
        Node idAttrNode = webViewHtml.getEngine().getDocument().createAttribute(attrName);
        String attrValue = IdUtil.randomUUID();
        attrValue = attrValue.replaceAll("-","_");
        idAttrNode.setNodeValue(attrValue);
        return idAttrNode;
    }

    /**
     * 初始化dom树 和 蒙层
     * @param document
     * @param parentItem
     * @param nodeList
     */
    private void initDocumentTree(Document document, DomTreeItem<Node> parentItem, NodeList nodeList){
        if(null==nodeList){
            return;
        }
        int len = nodeList.getLength();
        if(len==0){
            return;
        }
        for(int i=0;i< len;i++){
            Node node = nodeList.item(i);
            String tagName = node.getNodeName().toLowerCase();
            if(tagName.equalsIgnoreCase("body")){
                // 增加蒙層組件
                Element maskEl = document.createElement("div");
                maskEl.setTextContent("");
                maskEl.setAttribute("id",maskDivId);
                maskEl.setAttribute("style","position:absolute;top:0px;left:0px;background-color: #4e72b8; opacity: 0.33;width:0px;height:0px;z-index:99999999;font-size:30px");
                node.appendChild(maskEl);
            }
            if(tagName.equals("#text")&&StringUtils.isEmpty(node.getNodeValue().trim())){
                continue;
            }
            DomTreeItem<Node> child = new DomTreeItem<>(node);
            child.setExpanded(false);
            parentItem.getChildren().add(child);
            initDocumentTree(document,child,node.getChildNodes());
        }
    }
}
